#include "sign.h"

struct SigPair {
    unsigned char *sigma1;
    size_t sigma1_len;
    unsigned char *sigma2;
    size_t sigma2_len;
} signature1;

X509_REQ *verify_cert_req(X509 *cert,EVP_PKEY *pkey){
    X509_REQ *cert_req = X509_REQ_new();
    X509_NAME *name = NULL;
          if(!cert_req){
       fprintf(stderr, "X509_REQ_new error.\n");
        X509_REQ_free(cert_req);
        X509_NAME_free(name);
                     return NULL;
    }
   if(!X509_REQ_set_pubkey(cert_req, pkey)||!(name = X509_REQ_get_subject_name(cert_req))||!X509_NAME_add_entry_by_txt(name, "C",  MBSTRING_ASC, (unsigned char *)"CN", -1, -1, 0)||!X509_NAME_add_entry_by_txt(name, "O",  MBSTRING_ASC, (unsigned char *)"icatest.org", -1, -1, 0)||!X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)"ICA", -1, -1, 0)||!    X509_REQ_sign(cert_req, pkey, EVP_sha256())){
          fprintf(stderr, "X509_REQ_gen error.\n");
          X509_REQ_free(cert_req);
          X509_NAME_free(name);
                          return NULL;
    }
    
        return cert_req;  
} 

 EVP_PKEY *read_pkey(const char *path){
     FILE *fp = fopen(path, "r");
          if (!fp) {
         perror("Error opening file");
                      return NULL;
     }
    EVP_PKEY *pkey = PEM_read_PrivateKey(fp, NULL, NULL, NULL);  
           if(!pkey){
         ERR_print_errors_fp(stderr);
         EVP_PKEY_free(pkey);
         fclose(fp);
                       return NULL;
    }
    
    fclose(fp);  
          return pkey;
 }
 
 X509 *read_certificate(const char *path) {
    FILE *fp = fopen(path, "r");
          if (!fp) {
         perror("Error opening file");
                       return NULL;
     }

     X509 *cert = PEM_read_X509(fp, NULL, NULL, NULL);
             if (!cert) {
         ERR_print_errors_fp(stderr);
         X509_free(cert);
         fclose(fp);
                        return NULL;
     }
     fclose(fp);
             return cert;
}

int verify_sigma2(X509 *cert, EVP_PKEY *pkey){
          int  ret = 0;
          const ASN1_BIT_STRING *sig;
          const X509_ALGOR *sigalg = NULL;  
    X509_get0_signature(&sig, &sigalg, cert);
          size_t siglen = 0;
    siglen = sig->length;
    //fprintf(stderr, "Sig length is %d.\n", siglen);
    signature1.sigma1_len = siglen;
    signature1.sigma1 = (unsigned char *)malloc(siglen);
         if (signature1.sigma1== NULL) {
        fprintf(stderr, "Memory allocation failed\n");
                     return ret ;
    }
    memcpy(signature1.sigma1,sig->data, siglen); 
    
          unsigned char*tbs = NULL;
          size_t tbs_len = i2d_re_X509_tbs(cert, &tbs);
    //fprintf(stderr, "TBS length is %d.\n", tbs_len);
          
    EVP_MD_CTX *mdctx =  EVP_MD_CTX_new();
    EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(),NULL, pkey);
    EVP_DigestVerifyUpdate(mdctx, tbs, tbs_len);
    ret = EVP_DigestVerifyFinal(mdctx, signature1.sigma1, signature1.sigma1_len);    
          if (ret == 1) {  
       //printf("Sigma2 verification success!\n");  
    } else {  
        fprintf(stderr,cRED "Hybrid cert verified failed." cNORM "\n"); 
    } 
    EVP_MD_CTX_free(mdctx); 
    ret = 1;
          return ret;

}

int verify_sigma1(X509 *cert, EVP_PKEY *pkey, X509_REQ *cert_req){
//  
          int ret = 0;
          int index = X509_get_ext_by_NID(cert, NID_subject_alt_name , -1);
          if(index == -1){
        fprintf(stderr, "Failed to find nid from ext.\n");
                      return ret;         
    }  
    
    X509_EXTENSION *ext = X509_get_ext(cert, index);
    ASN1_OCTET_STRING *data2 = X509_EXTENSION_get_data(ext);
          size_t Sigma1_len = data2->length;
           unsigned char*Sigma1 = (unsigned char*)malloc(Sigma1_len);
    memcpy(Sigma1, data2->data, Sigma1_len);
    
    X509 *x509 = X509_new();
    X509_NAME *name = X509_NAME_new();
    
   if(!X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)"CA", -1, -1, 0)||
  !X509_NAME_add_entry_by_txt(name, "O",  MBSTRING_ASC, (unsigned char *)"catest.org", -1, -1, 0)||!X509_NAME_add_entry_by_txt(name, "C",  MBSTRING_ASC, (unsigned char *)"CN", -1, -1, 0)){
        fprintf(stderr, "X509 NAME gen error.\n");
                     return ret;
    }          
    ASN1_INTEGER_set(X509_get_serialNumber(x509),1);
    X509_gmtime_adj(X509_getm_notBefore(x509), 0);
    X509_gmtime_adj(X509_getm_notAfter(x509), 31536000L);
          
    X509_set_subject_name(x509, X509_REQ_get_subject_name(cert_req));
    X509_set_issuer_name(cert, name);    
    X509_set_pubkey(x509, X509_REQ_get_pubkey(cert_req)); 
    
          if(!X509_sign(x509, pkey,EVP_sha256())){
        fprintf(stderr, "Sigma1_cert_gen error.\n");
        X509_free(x509);
                     return ret;
    }
 //   fprintf(stderr, "Sigma1_cert_gen success.\n");
    //
          const ASN1_BIT_STRING *sig;
          const X509_ALGOR *sigalg = NULL;  
    X509_get0_signature(&sig, &sigalg, x509);
    
          size_t siglen = 0;
    siglen = sig->length;
   // fprintf(stderr, "Sig length is %d.\n", siglen);
           unsigned char *sigma1 = NULL;
           sigma1 = (unsigned char *)malloc(siglen);
         if (sigma1== NULL) {
        fprintf(stderr, "Memory allocation failed\n");
                  return 0;
    }
    memcpy(sigma1,sig->data, siglen);    
          unsigned char*tbs = NULL;
          size_t tbs_len = i2d_re_X509_tbs(x509, &tbs);
  //  fprintf(stderr, "TBS length is %d.\n", tbs_len);
    
             
    EVP_MD_CTX *mdctx =  EVP_MD_CTX_new();
    EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(),NULL, pkey);
    EVP_DigestVerifyUpdate(mdctx, tbs, tbs_len);
    ret = EVP_DigestVerifyFinal(mdctx, sigma1, siglen);
    
    
          if (ret == 1) {  
        //printf("Sigma1 verification success!\n");  
    } else {  
         fprintf(stderr,cRED "Hybrid cert verified failed." cNORM "\n");  
    }
          
          return ret;
}

int verify_hybrid_cert(OSSL_LIB_CTX *libctx,const char *signames,char * key_addr){

    int ret = 1;
    EVP_PKEY *class_pkey = NULL;
    EVP_PKEY *pq_pkey = NULL;
    X509 *cert = X509_new();
    char classprivkeypath[300];
    char oqsprivkeypath[300];
    char certpath[300];
    char keypath[300];
    const char *sep = "/";   

    sprintf(classprivkeypath, "%s%s%s%s", key_addr, sep, signames, "_cls.key");
    sprintf(oqsprivkeypath, "%s%s%s%s", key_addr, sep, signames, "_oqs.key");
    sprintf(certpath, "%s%s%s%s", key_addr, sep, signames, ".crt");
    
    cert = read_certificate(certpath);
    class_pkey = read_pkey(classprivkeypath);
    pq_pkey = read_pkey(oqsprivkeypath);
      
    signature1.sigma1 = NULL;
    signature1.sigma2 = NULL;
    signature1.sigma1_len = 0;
    signature1.sigma2_len = 0;
    
    X509_REQ *cert_req = X509_REQ_new();
    cert_req = verify_cert_req(cert, pq_pkey); 
    verify_sigma2(cert, pq_pkey);    
    //verify_sigma1(cert, class_pkey , cert_req);
   
    ret = 0;

    fprintf(stderr,cGREEN "Hybrid cert verified success." cNORM "\n");  
    EVP_PKEY_free(class_pkey);
    EVP_PKEY_free(pq_pkey);
        return ret;
}


